package edu.drexel.cs544.group11.client.program;

import java.io.IOException;
import java.net.ConnectException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

import edu.drexel.cs544.group11.client.protocol.ClientBSP;
import edu.drexel.cs544.group11.common.IllegalBSPStateException;

/**
 * UI
 * 
 * Represents the client command line interface for the BSP client program.
 * 
 * CS 544
 * 
 * @author Group 11:
 * @author Jason Setzer
 * @author Tam Thongsima
 * @author Kenny Oyefara
 * @author Chase Miller
 * 
 * @date Spring 2013
 * 
 */
public class ClientCLI {
    
    private ClientBSP clientBSP;
    
    private Scanner scan = new Scanner(System.in);
    
    // User messages
    private final String genericPrompt = "Please select a numeric command and press enter.";
    private final String genericError = "You have entered an incorrect command number.  Please try again.";
    private final String genericConnectionError = "An unknown connection error occured.  Please try again.";
    private final String transitionToSecureReadyError = "Could not transition to secure ready.";
    private final String transitionToPriceCheckError = "Could not transition to price check";
    private final String transitionToCustomerSessionError = "Could not transition to customer session";
    
    /**
     * Creates a client object.
     * 
     * @param clientBSP - the instance of the BSP object for the client.
     */
    public ClientCLI(ClientBSP clientBSP) {
	this.clientBSP = clientBSP;
	this.startup();
	try {
	    this.mainMenu();
	}
	catch (IllegalBSPStateException e) {
	    System.out.println("A BSP protocol state exception was detected.  Exiting the program...");
	    System.exit(0);
	}
    }
    
    /**
     * Performs all necessary startup for the client.
     */
    public void startup() {
	boolean shouldContinue = true;
	
	System.out.println("*** Welcome to the BSP Client. *** ");
	do {
	    System.out.println("To connect to the server, please press enter.  To exit, type 'exit' and press enter.");
	    
	    String input = this.scan.nextLine();
	    System.out.println("");
	    
	    if (this.isExitIdentifier(input)) {
		this.exitProgram();
	    }
	    else {
		try {
		    this.clientBSP.connect();
		    System.out.println("\n");
		    shouldContinue = false;
		}
		catch (ConnectException e) {
		    System.out.println("Could not connect to the server.  Try again or exit.\n");
		}
		catch (IllegalBSPStateException e) {
		    System.out.println(e);
		}
	    }
	}
	while (shouldContinue);
    }
    
    /**
     * Menu for main menu (i.e. secure_session).
     * 
     * @throws IllegalBSPStateException
     */
    private void mainMenu() throws IllegalBSPStateException {
	boolean shouldLoop = true;
	
	do {
	    System.out.println("");
	    System.out.println("*** BSP Main Menu ***");
	    System.out.println(this.genericPrompt);
	    
	    int count = 1;
	    String output = "";
	    
	    output += count++ + " - Start a new order\n";
	    output += count++ + " - Scan an item to check price and details\n";
	    output += count++ + " - Login as admin\n";
	    output += count++ + " - Exit\n";
	    
	    System.out.println(output);
	    
	    String userInput = this.scan.nextLine();
	    
	    try {
		int inputAsInt = Integer.parseInt(userInput);
		
		switch (inputAsInt) {
		    case 1:
			if (this.clientBSP.tryTransitionToCustomerSession()) {
			    this.customerSessionMenu();
			    shouldLoop = false;
			}
			else {
			    System.out.println(this.transitionToCustomerSessionError);
			}
			break;
		    case 2:
			if (this.clientBSP.tryTransitionToPriceCheck()) {
			    this.itemInfoMenu();
			    shouldLoop = false;
			}
			else {
			    System.out.println(this.transitionToPriceCheckError);
			}
			break;
		    case 3:
			System.out.println("Please enter your employee ID and press enter.");
			
			String input = this.scan.nextLine();
			
			if (input != null && !input.equals("")) {
			    if (this.clientBSP.tryTransitionToAdminSession(input)) {
				this.adminMenu();
				shouldLoop = false;
			    }
			    else {
				System.out.println("Could not login.");
			    }
			}
			else {
			    System.out.println("You did not enter an employee ID.  Returning to main menu.");
			    System.out.println("");
			    shouldLoop = true;
			}
			break;
		    case 4:
			this.exitProgram();
			shouldLoop = false;
			break;
		    default:
			// This should error and return to loop.
			System.out.println(this.genericError);
			break;
		}
	    }
	    catch (NumberFormatException e) {
	    }
	}
	while (shouldLoop);
    }
    
    /**
     * Menu for admin functions.
     * 
     * @throws IllegalBSPStateException
     */
    private void adminMenu() throws IllegalBSPStateException {
	boolean shouldLoop = true;
	
	do {
	    System.out.println("");
	    System.out.println("*** Admin Main Menu ***");
	    System.out.println(this.genericPrompt);
	    int count = 1;
	    String output = "";
	    
	    output += count++ + " - Scan a product for info\n";
	    output += count++ + " - Update existing item\n";
	    output += count++ + " - Add new item to inventory\n";
	    output += count++ + " - Cancel and return to main menu\n";
	    output += count++ + " - Exit\n";
	    
	    System.out.println(output);
	    
	    String userInput = this.scan.nextLine();
	    
	    try {
		int inputAsInt = Integer.parseInt(userInput);
		
		switch (inputAsInt) {
		    case 1:
			this.itemInfoUI();
			shouldLoop = true;
			break;
		    case 2:
			this.adminUpdateItemUI();
			shouldLoop = true;
			break;
		    case 3:
			this.adminAddProductUI();
			shouldLoop = true;
			break;
		    case 4:
			if (this.clientBSP.tryTransitionToSecureReady()) {
			    this.mainMenu();
			    shouldLoop = false;
			}
			else {
			    System.out.println(this.transitionToSecureReadyError);
			}
			break;
		    case 5:
			this.exitProgram();
			shouldLoop = false;
		    default:
			// This should error and return to loop.
			System.out.println(this.genericError);
			break;
		}
	    }
	    catch (NumberFormatException e) {
	    }
	}
	while (shouldLoop);
    }
    
    /**
     * Menu for customer session.
     * 
     * @throws IllegalBSPStateException
     */
    private void customerSessionMenu() throws IllegalBSPStateException {
	boolean shouldLoop = true;
	
	do {
	    System.out.println("");
	    System.out.println("*** Customer Main Menu ***");
	    System.out.println(this.genericPrompt);
	    
	    int count = 1;
	    String output = "";
	    
	    output += count++ + " - Scan items for info\n";
	    output += count++ + " - Add Item\n";
	    output += count++ + " - Remove Item\n";
	    output += count++ + " - Finish and pay\n";
	    output += count++ + " - Cancel order and return to main menu\n";
	    output += count++ + " - Exit\n";
	    
	    System.out.println(output);
	    
	    String userInput = this.scan.nextLine();
	    
	    try {
		int inputAsInt = Integer.parseInt(userInput);
		
		switch (inputAsInt) {
		    case 1:
			this.cartScanItemInfoMenu();
			shouldLoop = true;
			break;
		    case 2:
			this.addItemToCart();
			shouldLoop = true;
			break;
		    case 3:
			this.removeItemFromCart();
			shouldLoop = true;
			break;
		    case 4:
			if (this.clientBSP.tryTransitionToCustomerSession()) {
			    if (this.clientBSP.tryTransitionToPurchasing()) {
				if (this.finishAndPay()) {
				    shouldLoop = false;
				    this.mainMenu();
				}
				else {
				    shouldLoop = true;
				}
			    }
			}
			else {
			    System.out.println("Could not transition into a purchasing state.");
			}
			break;
		    case 5:
			if (this.clientBSP.tryTransitionToSecureReady()) {
			    this.mainMenu();
			    shouldLoop = false;
			}
			else {
			    System.out.println(this.transitionToSecureReadyError);
			}
			break;
		    case 6:
			this.exitProgram();
			shouldLoop = false;
			break;
		    default:
			// This should error and return to loop.
			System.out.println(this.genericError);
			break;
		}
	    }
	    catch (NumberFormatException e) {
	    }
	}
	while (shouldLoop);
    }
    
    /**
     * Menu for getting an item's info.
     * 
     * @throws IllegalBSPStateException
     */
    private void itemInfoMenu() throws IllegalBSPStateException {
	boolean shouldLoop = true;
	
	do {
	    System.out.println("");
	    System.out.println("*** Item Info Menu ***");
	    System.out.println(this.genericPrompt);
	    
	    int count = 1;
	    String output = "";
	    
	    output += count++ + " - Scan a product\n";
	    output += count++ + " - Cancel price check and return\n";
	    output += count++ + " - Exit\n";
	    
	    System.out.println(output);
	    
	    String userInput = this.scan.nextLine();
	    
	    try {
		int inputAsInt = Integer.parseInt(userInput);
		
		switch (inputAsInt) {
		    case 1:
			this.itemInfoUI();
			shouldLoop = true;
			break;
		    case 2:
			if (this.clientBSP.tryTransitionToSecureReady()) {
			    this.mainMenu();
			    shouldLoop = false;
			}
			else {
			    System.out.println(this.transitionToSecureReadyError);
			}
			break;
		    case 3:
			this.exitProgram();
			shouldLoop = false;
			break;
		    default:
			// This should error and return to loop.
			System.out.println(this.genericError);
			break;
		}
	    }
	    catch (NumberFormatException e) {
	    }
	}
	while (shouldLoop);
    }
    
    /**
     * Menu for cart scan item info.
     * 
     * @throws IllegalBSPStateException
     */
    private void cartScanItemInfoMenu() throws IllegalBSPStateException {
	boolean shouldLoop = true;
	
	do {
	    System.out.println("");
	    System.out.println("*** Item Info Menu ***");
	    System.out.println(this.genericPrompt);
	    
	    int count = 1;
	    String output = "";
	    
	    output += count++ + " - Scan a product\n";
	    output += count++ + " - Cancel price check and return\n";
	    output += count++ + " - Exit\n";
	    
	    System.out.println(output);
	    
	    String userInput = this.scan.nextLine();
	    
	    try {
		int inputAsInt = Integer.parseInt(userInput);
		
		switch (inputAsInt) {
		    case 1:
			this.itemInfoUI();
			shouldLoop = true;
			break;
		    case 2:
			if (this.clientBSP.tryTransitionToCustomerSession()) {
			    this.customerSessionMenu();
			    shouldLoop = false;
			}
			else {
			    System.out.println(this.transitionToCustomerSessionError);
			}
			break;
		    case 3:
			this.exitProgram();
			shouldLoop = false;
			break;
		    default:
			// This should error and return to loop.
			System.out.println(this.genericError);
			break;
		}
	    }
	    catch (NumberFormatException e) {
	    }
	}
	while (shouldLoop);
    }
    
    /**
     * UI for admin to add new item to inventory
     * 
     * @throws IllegalBSPStateException
     */
    private void adminUpdateItemUI() throws IllegalBSPStateException {
	
	boolean success = false;
	
	String barcode = "";
	String element = "";
	String value = "";
	
	while (barcode.isEmpty()) {
	    System.out.println("Enter the barcode for an item to update and press enter.");
	    barcode = this.scan.nextLine();
	}
	
	while (element.isEmpty()) {
	    System.out.println("Select a number for the type of info you want to update this item with.");
	    System.out.println("1 - Category");
	    System.out.println("2 - Name");
	    System.out.println("3 - Price");
	    System.out.println("4 - Quantity");
	    
	    element = this.scan.nextLine();
	    
	    Integer number = null;
	    try {
		number = Integer.parseInt(element);
	    }
	    catch (NumberFormatException e) {
	    }
	    
	    if (number != null) {
		switch (number) {
		    case 1:
			element = "category";
			break;
		    case 2:
			element = "name";
			break;
		    case 3:
			element = "price";
			break;
		    case 4:
			element = "quantity";
			break;
		    default:
			System.out.println("Invalid entry selected");
			element = ""; // force to loop again.
			break;
		}
		
	    }
	    else {
		System.out.println("Invalid entry selected");
		element = "";
	    }
	}
	
	while (value.isEmpty()) {
	    System.out.println("Enter the new value for this type of info.");
	    value = this.scan.nextLine();
	}
	
	success = this.clientBSP.tryUpdateItem(barcode, element, value);
	
	if (success) {
	    System.out.println("The item was updated successfully");
	}
	else {
	    System.out.println("The item failed to update.");
	}
    }
    
    /**
     * Returns true if the provided string is an exit identifier.
     * 
     * @param itemInfo
     * @return
     */
    private boolean isExitIdentifier(String itemInfo) {
	boolean isExit = itemInfo.equalsIgnoreCase("exit");
	return isExit;
    }
    
    /**
     * UI for admin to add a new product.
     * 
     * @throws IllegalBSPStateException
     */
    private void adminAddProductUI() throws IllegalBSPStateException {
	boolean success = false;
	
	String barcode = "";
	String category = "";
	String name = "";
	String price = "";
	String quantity = "";
	
	while (barcode.isEmpty()) {
	    System.out.println("Enter the barcode for the new item and press enter.");
	    barcode = this.scan.nextLine();
	}
	
	while (category.isEmpty()) {
	    System.out.println("Enter the category for the new item and press enter.");
	    category = this.scan.nextLine();
	}
	
	while (name.isEmpty()) {
	    System.out.println("Enter the name for the new item and press enter.");
	    name = this.scan.nextLine();
	}
	
	while (price.isEmpty()) {
	    System.out.println("Enter the price for the new item and press enter.");
	    price = this.scan.nextLine();
	}
	
	while (quantity.isEmpty()) {
	    System.out.println("Enter the quantity for the new item and press enter.");
	    quantity = this.scan.nextLine();
	}
	
	success = this.clientBSP.tryAddInventoryItem(barcode, category, name, price, quantity);
	
	if (success) {
	    System.out.println("The item was added successfully");
	}
	else {
	    System.out.println("Could not add the item.");
	}
    }
    
    /**
     * UI to finish and pay.
     */
    private boolean finishAndPay() throws IllegalBSPStateException {
	boolean success = false;
	
	String creditCard = "";
	String name = "";
	String vendor = "";
	Date expirationDate = null;
	
	while (creditCard.isEmpty()) {
	    System.out.println("Enter your credit card number and press enter.");
	    creditCard = this.scan.nextLine();
	}
	
	while (name.isEmpty()) {
	    System.out.println("Enter the name on your card and press enter.");
	    name = this.scan.nextLine();
	}
	
	while (vendor.isEmpty()) {
	    System.out.println("Enter the card type (visa, mastercard, etc) and press enter.");
	    vendor = this.scan.nextLine();
	}
	
	while (expirationDate == null) {
	    SimpleDateFormat format = new SimpleDateFormat("mm-yyyy");
	    System.out.println("Enter card's expiration date (in format mm-yyyy) and press enter.");
	    String dateAsString = this.scan.nextLine();
	    try {
		expirationDate = format.parse(dateAsString);
	    }
	    catch (IllegalArgumentException | ParseException e) {
	    }
	}
	
	success = this.clientBSP.tryPay(creditCard, name, vendor, expirationDate);
	
	if (success) {
	    System.out.println("Purchase complete.");
	}
	else {
	    System.out.println("Could not complete purchase.");
	}
	
	return success;
    }
    
    /**
     * UI to add an item to the cart.
     */
    private void addItemToCart() throws IllegalBSPStateException {
	boolean success = false;
	
	String barcode = this.getBarcodeFromUser();
	success = this.clientBSP.tryAddItem(barcode);
	
	if (success) {
	    System.out.println("Item successfully added to cart.");
	}
	else {
	    System.out.println("Could not add the item to the cart.");
	}
    }
    
    /**
     * UI to remove an item from the cart.
     */
    private void removeItemFromCart() throws IllegalBSPStateException {
	boolean success = false;
	
	String barcode = this.getBarcodeFromUser();
	success = this.clientBSP.tryRemoveItem(barcode);
	
	if (success) {
	    System.out.println("Item successfully removed from cart.");
	}
	else {
	    System.out.println("Could not remove the item from the cart.");
	}
    }
    
    /**
     * UI for item info.
     * 
     * @throws IllegalBSPStateException
     */
    private void itemInfoUI() throws IllegalBSPStateException {
	String response = "";
	try {
	    String barcode = this.getBarcodeFromUser();
	    response = this.clientBSP.scanForItemInfo(barcode);
	    System.out.println(response);
	    System.out.println("");
	}
	catch (IOException e) {
	    System.out.println(this.genericConnectionError);
	}
	catch (Error e) {
	    System.out.println("Item info could not be found for the barcode entered.");
	}
    }
    
    /**
     * Promts the user for the barcode and returns the entered input.
     * 
     * @return - Returns the barcode specified by the user.
     */
    private String getBarcodeFromUser() {
	String barcode = "";
	
	System.out.println("Enter a barcode and press enter.");
	barcode = this.scan.nextLine();
	
	return barcode;
    }
    
    /**
     * Answers exiting the program.
     */
    private void exitProgram() {
	System.out.println("Exiting program...");
	
	try {
	    this.clientBSP.endSession();
	}
	catch (IllegalBSPStateException e) {
	}
	
	System.out.println("Goodbye");
	System.exit(0);
    }
}
